package com.abel.bigwater.dataserver.model.h86a

import com.abel.bigwater.data.BcdString
import com.abel.bigwater.dataserver.model.PacketCmdException
import com.abel.bigwater.dataserver.model.PacketException
import com.abel.bigwater.dataserver.model.PacketHeaderTailException
import com.abel.bigwater.dataserver.model.PacketNotEnoughException
import com.abel.bigwater.dataserver.model.hgd.HgdaoBase
import io.netty.buffer.ByteBuf
import org.slf4j.LoggerFactory
import kotlin.experimental.and

/**
 * <pre>
 * H86: AA AA
 * FA
 * 27 11 // 10001 ShortBE
 * 16 09 11 17 20
 * 1B 44 02 95 88 11 00 00 00 00 00 00 00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 12 00 00 00 00 00 00 00 00 00 00 00
 * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 * 00 00 00 00 00 00 00 00 00 00 13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 33 00 15 00 14 00 11
 * 00 13 00 13 00 16 00 15 00 15 00 15 00 15 00 15 00 15 00 14 00 15 00 15 00 14 00 12 00 16 00 17 00 15 00 16 00 16 00
 * 15 00 15 00 15 00 15 00 16
 * 0D 0A
 *
 * AA AA 7E 00 66 5 10 24 16 15 C 31 3 1A 5 11
 * 数据头 长度 站号 年 月 日 时 分 个数 间隔 电池电压 短信次数 类型与通道号
 * 0 0 0 0 0 0 7 37 7 1D 7 1D 7 1E 7 1E 7 1E 7 1E 7 1D
 * 昨日流量 累计流量 增加量 增加量 增加量 增加量 增加量 增加量 增加量
 * 7 1E 7 1E 7 1E 7 1C 12 0 0 0 0 0 0 7 37 7 1D 7 1D
 * 增加量 增加量 增加量 增加量 类型与通道号 昨日流量 累计流量 增加量 增加量
 * 7 1E 7 1E 7 1E 7 1E 7 1D 7 1E 7 1E 7 1D 7 1C 43
 * 增加量 增加量 增加量 增加量 增加量 增加量 增加量 增加量 增加量 类型与通道号
 * 31 35 1 FB 1 FB 1 FB 1 FB 1 FB 1 FB 1 FB 1 FA
 * 状态 类型与通道号 模拟量 模拟量 模拟量 模拟量 模拟量 模拟量 模拟量 模拟量
 * 1 FC 1 FB 1 FB 1 FB 36 1 FC 1 FC 1 FC 1 FC 1 FC
 * 模拟量 模拟量 模拟量 模拟量 类型与通道号 模拟量 模拟量 模拟量 模拟量 模拟量
 * 1 FB 1 FC 1 FC 1 FC 1 FC 1 FC 1 FC 0D 0A
 * 模拟量 模拟量 模拟量 模拟量 模拟量 模拟量 模拟量
 *
 * 部分注解：
 *  长度――从站号到数据结束的数据个数。
 *  个数――该包文所包含每个传感的数据个数。
 *  间隔――数据在时间上的间隔，如31(15分钟)为间隔，即第一个数据是16:15时刻的，那下一个数据是在16:30的。
 * 31—15分钟 32—30分钟 33—1小时 34—2小时 35—4小时
 * 36—6小时 37—12小时 38—24小时
 *  类型与通道号――高字节表示类型，低字节表示通道号。
 * 类型：1—脉冲量 2—开关量 3—模拟量 4—报警量。
 *  昨日流量――以最后一时刻为基准的前一天的日流量，从23日0点到24日0点的流量。
 *  累计流量――起始时间（10/24 16:15）的累计流量。
 *  增加量――在上一时刻流量的基础上增加的量。
 *
</pre> *
 *
 * @author Abel
 */
class H86AHistoryData : HgdaoBase {

    /** Not same as its parent class: it's not in minutes but codeBcd.  */
    /**
     * @return the h86APeriod
     */
    /**
     * @param h86aPeriod
     * the h86APeriod to set
     */
    var h86APeriod: H86APeriodType? = null

    var channelList = ArrayList<H86AChannelBase<*>>()

    // END: \r\n

    constructor() : super(HgdaoBase.TYPE_TCP)

    /**
     * @param type
     */
    constructor(type: String) : super(type)

    @Throws(PacketException::class)
    override fun parseBuffer(buf: ByteBuf, ignoreChecksum: Boolean): Boolean {
        buf.duplicate().apply {
            // AA AA
            val h1 = readByte()
            val h2 = readByte()
            if (h1 != HEAD_1 || h2 != HEAD_2) {
                return false
            }
        }

        if (buf.readableBytes() < 2 + 1 + MIN_DATA_LENGTH + TAIL_LENGTH) {
            throw PacketNotEnoughException("not enough data: ${buf.readableBytes()}")
        }

        buf.duplicate().apply {
            skipBytes(2)
            length = readUnsignedByte().toInt()
            if (readableBytes() < length + 2) {
                throw PacketNotEnoughException("not enough bytes: ${buf.readableBytes()}.")
            }
        }

        buf.readBytes(3 + length + 2).apply {
            temp = ByteArray(readableBytes()).also { duplicate().readBytes(it) }
            fmt = BcdString.hexString(temp!!, true)

            // AA AA len
            skipBytes(3)

            stationInt = readUnsignedShort()
            station = String.format("%1$05d", stationInt)

            startTimeStr = BcdString(ByteArray(5).also { readBytes(it) }, true)

            count = readUnsignedByte()

            // period
            h86APeriod = H86APeriodType.findByValue(readUnsignedByte())

            // voltage
            voltage = readUnsignedShort()

            // upload times.
            uploadTimes = readUnsignedByte().toInt()

            // channelId
            while (readableBytes() > 2) {
                val hctype = H86AChannelBase.parseType(this)

                val hc =
                        when (hctype) {
                            H86AChannelType.PulseType -> {
                                H86APulseChannel(count)
                            }

                            H86AChannelType.AnalogType -> H86AAnalogChannel(count)

                            H86AChannelType.WarnType -> H86AWarnChannel()

                            else -> throw PacketCmdException("unknown channel: $hctype")
                        }
                hc.parseBuffer(this.readBytes(hc.bufLength()))
                channelList.add(hc)
            }

            // verify tail.
            val t1 = readByte()
            val t2 = readByte()

            if (t1 != 0x0D.toByte() || t2 != 0x0A.toByte()) {
                throw PacketHeaderTailException(String.format("bad tail: %1$02X %2$02X", t1, t2))
            }

        }

        return true
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    override fun toString(): String {
        return String.format(
                "%s {period:%s, channelList:%s, len:%s, station:%s, startTime:%s, count:%s, voltage:%s, uploadTimes:%s}",
                javaClass.simpleName,
                h86APeriod, channelList.joinToString(), length, station, startTime, count, voltage, uploadTimes)
    }

    companion object {
        private val logger = LoggerFactory.getLogger(H86AHistoryData::class.java)

        private val TAIL_LENGTH = 2

        /**
         * No checksum.
         */
        private val MIN_DATA_LENGTH = 2 + 5 + 1 + 2 + 1

        // start[H]: AA AA.
        private val HEAD_1 = 0xAA.toByte()
        private val HEAD_2 = 0xAA.toByte()
    }
}
